#makefile

#第四章 Makefike的规则

#终极目标：一般是makefile文件中的第一个规则将被视为终极目标\
但是以下两种情况下列外：\
1、目标名以 . 开始，并且其后没有斜线 / \
2、模式规则的目标。

#例子：
foo.o:foo.c defs.h
	cc -c -g foo.c
#规则的语法
TARGETS:PREREQUISITES
	COMMAND
	...
#或者
TARGETS:PREREQUISITES;COMMAND
	COMMAND
	...

#规则书写需要注意的点
#1、命令在依赖的下面，以 [tab]另起一行，或者与目标，依赖在同一行，以 ；号隔开
#2、Makefile中符号 "$"有特殊的含义（表示变量或者函数的引用），在规则中需要使用符号"$"的地方，需要书写两个连续的（"$$"）
#3、较长的行可以使用，反斜线“\”将其书写到下一行

#总之规则的核心思想就是：目标文件的内容是由依赖文件决定，依赖文件的任何一处改动，\
将导致目前已经存在的目标文件的内容过期.规则的命令为重建目标提供了方法。这些命令 \
运行在系统的shell上。

#依赖的类型：常规依赖，order-only依赖；用 管道 “|”符号隔开，管道左边为常规依赖，右边为order-only依赖
TARGETS:NORMAL-PREREQUISITES|ORDER-ONLY-PREREQUISITES
#order-only依赖的使用举例
LIBS=libtest.a
foo:foo.c|$(LIBS)
	$(CC) $(CFLAGS) $<-O $@ $(LIBS)
#make在执行这个规则时，如果目标文件"foo"已存在。当“foo.c”被修改以后，目标“foo”将被重建，\
但是当“libtest.a”被修改以后。将不执行规则的命令来重建目标“foo.c"。"libtest.a"只有在目标文件不存在的情况下，\
才会参与规则的执行。

#文件名使用通配符
#"*"、"?"和"[...]"
#通配符的使用场合： \
1、可以用在规则的目标、依赖中 \
2、可出现在规则的命令中，通配符的通配处理是在shell在执行次命令时完成的\
3、其他地方不允许直接使用通配符，而是需要通过函数wildcard来实现

#注意，在文件名中，如果存在通配符，那么需要使用反斜线“\”转义\
以波浪线“~”开始的文件名有特殊含义。代表家目录。

#通配符使用举例
clean:
	rm -f *.O

print:*.c
	lPR -p $?
	touch print
#上述规则中目标“print"是一个空目标文件，只是记录最后一次执行此规则的时间\
自动环境变量"?",在这里表示依赖文件列表中被改变过的所有文件
#变量定义中使用的通配符不被通配处理，比如
objects=*.o #objects表示的值是 "*.o",并不是所有.o文件的集合，需要如下写法：
objects=$(wildcard *.o)

#通配符存在的缺陷
objects=*.O

foo:$(objects)
	cc -o foo $(CFLAGS) $(objects)
#这里变量"objecs"的值是一个字符串"*.o"。在重建"foo"的规则中对变量"objects"进行展开，\
目标"foo"的依赖就是"*.o"，即所有的.o文件的列表。如果在工作目录下已经存在必需的.o文件，\
那么这些.o文件将成为目标的依赖文件，目标"foo"将根据规则被重建\
但是如果工作目录下所有的.o文件都被删除，重新执行make将会得到一个类似于“没有创建*.o文件的规则”的错误提示。\
为了解决这个问题，我们需要使用一些高级的技巧，包括使用"wildcard"函数，objects=$(wildcard *.o)\
实现字符串的置换。

#函数wildcard:$(wildcard PATTERN),在makefile中，它被展开为已经存在的、使用空格分开的、匹配此模式的所有文件列表\
如果不存在任何符合此模式的文件，函数会忽略模式字符并返回空
#一般我们可以使用$(wildcard *.c)来获取工作目录下的所有的.c文件列表\
使用复杂一点：$(patsubst %.c,%.o,$(wildcard *c)),首先使用wildcard函数获取工作目录下的.c文件列表，\
之后将列表中所有文件名的后缀.c替换为.o。这样我们就可以得到在当前目录下生成的.o文件列表。\
因此在一个目录下可以使用如下内容的makefile来将工作目录下的所有的.c文件进行编译并最后连接成为一个可执行文件
objects:=$(patsubst %.c,%.o,$(wildcard *.c))

foo:$(objects)
	cc -o foo $(objects)

#目录搜寻
#应用于在指定的若干目录下自动搜索依赖文件
#一般搜索（变量VPATH)：GNU make 可以识别一个特殊变量"VPATH",通过变量“VPATH"可以指定依赖文件的搜索路径。\
例如对变量定义如下：
VPATH=src:../headers
#VPATH变量指定的路径在makefile中对所有文件有效

#选择性搜索（关键字vpath）：这个不是变量，是make的关键字，有3种使用方法：\
1.vpath PATTERN DIRECTORIES :为所有符合模式 PATTERN 的文件指定搜索目录DIRECTORIES,\
多个目录使用空格或者冒号(:)分开。类似上一小节的"VPATH"\
2.vpath PATTERN :清除之前为符合模式“PATTERN”的文件设置的搜索路径\
3.vpath :清除所有已被设置的文件搜索路径

#模式字符"%","%"的意思是匹配一个或者多个字符。%.h表示所有以.h结尾的文件。如果 PATTERN 中没有包含模式字符%,\
那么它就是一个明确的文件名。 
vpath %.h ../headers
#上面表示，Makefile中出现的.h文件，如果不能在当前目录下找到，则到目录../headers下寻找。\
注意这里指定的；路径仅限于在Makefile文件内容中出现的.h文件。而不是.c文件中包含的.h文件，这个是要使用gcc 的 -l选项来指定

#多个vpath 处理，不包含工作目录，因为对工作目录的处理永远处于最优地位
vpath %.c foo
vpath % blish
vpath %.c bar
#表示对所有的.c文件，make依次查找的目录为：foo  blish  bar \
而： 
vpath %.c foo: bar
vpath % blish
#对于所有的.c文件make将依次查找目录：foo  bar  blish

#目录搜索的机制
#make在解析Makefile文件执行规则时对文件路径保存或废弃所依据的算法如下：\
1、首先，如果规则的目标文件在makefile文件所在的目录（工作目录）下不存在，那么就执行目录搜索\
2、如果目录搜索成功，在指定的目录下存在此规则的目标。那么搜索到的完整的路径名就被作为临时的目标文件被保存\
3、对于规则中的所有依赖文件使用相同的方法处理\
4、完成第三步的依赖处理后，make程序就可以决定规则的目标是否需要重建，两种情况后续处理如下：\
a)规则的目标不需要重建：那么就是规则的目标和依赖的文件路径有效\
b)规则的目标需要重建：那么目录搜索得到的目标文件的完整的路径名无效，那么将在工作目录下重建该目标。\
依赖的路径名是不会失效的（可以使用GPATH变量来改善）

#存在porm目录，包含src目录和makefile文件，src目录下包含sum.c和memcp.c文件，下面为makefile文件内容
LIBS=libtest.a
VPATH=src

libtest.a:sum.o memcp.O
	$(AR) $(ARFLAGS) $@ $^

#如果porm目录下没有libtest.a文件，那么make就会去寻找src目录下，如果也没有那么就会在porm目录下面重建\
如果有，且不需要被重建，那么不执行命令，如果需要被重建那么将会在porm目录下面被重建，这样在porm目录和\
src目录下面都会有libtest.a文件，只是porm目录下是最新的。

#加了GPATH后
LIBS=libtest.a
GPATH=src
VPATH=src
LDFLAGS+=-L ./. -ltest

#在src和porm目录下都没有libtest.a文件那么在porm下重建，如果在src中有，那么就在src下重建

#命令行和搜索目录 --- 自动化变量：$^代表所有通过目录搜索得到的依赖文件的完整；路径名（目录+一般文件名）列表\
$@代表规则的目标。所以对于一个规则我们可以进行如下的描述：
foo.o:foo.c
	cc -c $(CFLAGS) $^ -O $@

VPATH=src:../headers
foo.o:foo.c defs.h hack.h
	cc -c $(CFLAGS) $< -O $@
#$< 代表规则中通过目录搜索得到的依赖文件列表的第一个依赖文件

#隐含规则和搜索目录：同样适用上一条

#库文件后搜索目录：如果在依赖文件列表中存在一个 "-lNAME"形式的文件时：\
1、make在执行规则时会在当前目录下搜索一个名字为 libNAME.so的文件\
2、如果当前目录下不存在这样一个文件，则make会继续搜索使用VPATH或者vpath指定的搜索目录\
3、还是不存在，make将搜索系统库文件存在的默认目录，顺序是：/lib /usr/lib 和PREFIX/lib(在linux系统中为/usr/local/lib)\
4、如果libNAME.so通过以上途径最后还是没有找到的话，那么make将会按照以上的搜索顺序查找名字为libNAME.a的文件

foo:foo -lcurses
	cc $^ -o $@
#如果文件foo.c和/usr/lib/libcurses.a被更新，执行规则时将使用命令cc foo.c /usr/lib/libcurses.a -o foo

#在规则的依赖列表中出现 -lNAME 格式依赖中，表示需要搜索的依赖文件名为 libNAME.so 和 libNAME.a,\
这个是由变量.LIBPATTERNS指定的，默认情况下为lib%.so 和 lib%.a

#Makefile的伪目标\
1、避免在我们的makefile中定义的只执行命令的目标和工程目录下的实际文件出现名字冲突\
2、提高执行make时的效率，特别是对于一个大型的工程来说，编译的效率也许你同样关系
.PHONY:clean
clean:
	rm *.o temp


#版本1
SUBDIRS=foo bar baz

subdirs:
	for dir in $(SUBDIRS);do\
		$(MAKE) -C $$dir;\
	done

#版本2
SUBDIRS=foo bar baz

.PHONY:subdirs $(SUBDIRS)

subdirs:$(SUBDIRS)
$(SUBDIRS):
	$(MAKE) -C $@
foo:baz

#版本1
all:prog1 prog2 prog3
./PHONY:all

prog1:prog1.o utils.O
	cc -o prog1 prog1.o utils.O

prog2:prog2.o
	cc -o prog2 prog2.o

prog3:prog3.o sort.o utils.o
	cc -o prog3	prog3.o  sort.o utils.o

#版本1
.PHONY:cleanall cleanobj cleandiff
cleanall:cleanobj cleandiff
	rm program

cleanobj:
	rm *.o

cleandiff:
	rm *.diff

#内嵌隐含变量RM=rm -f。所以可以使用 $(RM)来代替rm


#强制目标（没有命令或依赖的规则）：总是被认为更新过的，如果强制目标作为依赖，那么这条规则总是被执行
clean:FORCE
	rm $(objecs)
FORCE:

#空目标文件
print:foo.c bar.c
	lpr -p $?
	touch print

#Makefile的特殊目标
.PHONY
.SUFFIXES
.DEFAULT
.PRECIOUS
.INTERMEDIATE
.SECONDARY
.DELETE_ON_ERROR
.IGNORE
.LOW_RESOLUTION_TIME
.SILENT
.EXPORT_ALL_VARIABLES
.NOTPARALLEL

#多目标依赖
bigoutput littleoutput:text.g
	generate text.g -$(subst output,,$@) > $@
#其等价于
bigoutput:text.g
	generate text.g -big > bigoutput
littleoutput:text.g
	generate text.g -little > littleoutput 		

































































































































































































